package naga.x.platform2.channel.weixin.support;

import java.io.Reader;
import java.io.StringReader;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.annotation.JSONField;

import naga.x.platform2.channel.weixin.WeiXinController;
import naga.x.platform2.utils.CommonUtils;
import naga.x.platform2.utils.IOUtils;
import naga.x.platform2.utils.NumberUtils;
import naga.x.platform2.utils.StringUtils;

/**
 * 微信支付结果<br/>
 * 由微信方回调传入
 * @author YangLi
 *
 */
public class PayResult {
	
	protected static final Logger LOG = LoggerFactory.getLogger(PayResult.class);

//	通知参数
//	字段名	变量名	必填	类型	示例值	描述
//	返回状态码	return_code	是	String(16)	SUCCESS	
	protected String return_code;
//	SUCCESS/FAIL
//	此字段是通信标识，非交易标识，交易是否成功需要查看result_code来判断
//	返回信息	return_msg	否	String(128)	签名失败	
	protected String return_msg;
//	返回信息，如非空，为错误原因
//	签名失败
//	参数格式校验错误
//	以下字段在return_code为SUCCESS的时候有返回
//
//	字段名	变量名	必填	类型	示例值	描述
//	公众账号ID	appid	是	String(32)	wx8888888888888888	微信分配的公众账号ID（企业号corpid即为此appId）
	protected String appid;
//	商户号	mch_id	是	String(32)	1900000109	微信支付分配的商户号
	protected String mch_id;
//	设备号	device_info	否	String(32)	013467007045764	微信支付分配的终端设备号，
//	随机字符串	nonce_str	是	String(32)	5K8264ILTKCH16CQ2502SI8ZNMTM67VS	随机字符串，不长于32位
//	签名	sign	是	String(32)	C380BEC2BFD727A4B6845133519F3AD6	签名，详见签名算法
	protected String sign;
//	业务结果	result_code	是	String(16)	SUCCESS	SUCCESS/FAIL
	protected String result_code;
//	错误代码	err_code	否	String(32)	SYSTEMERROR	错误返回的信息描述
//	错误代码描述	err_code_des	否	String(128)	系统错误	错误返回的信息描述
//	用户标识	openid	是	String(128)	wxd930ea5d5a258f4f	用户在商户appid下的唯一标识
	protected String openid;
//	是否关注公众账号	is_subscribe	否	String(1)	Y	用户是否关注公众账号，Y-关注，N-未关注，仅在公众账号类型支付有效
//	交易类型	trade_type	是	String(16)	JSAPI	JSAPI、NATIVE、APP
	protected String trade_type;
//	付款银行	bank_type	是	String(16)	CMC	银行类型，采用字符串类型的银行标识，银行类型见银行列表
	protected String bank_type;
//	订单金额	total_fee	是	Int	100	订单总金额，单位为分
	protected int total_fee;
//	应结订单金额	settlement_total_fee	否	Int	100	应结订单金额=订单金额-非充值代金券金额，应结订单金额<=订单金额。
//	货币种类	fee_type	否	String(8)	CNY	货币类型，符合ISO4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型
	protected String fee_type;
//	现金支付金额	cash_fee	是	Int	100	现金支付金额订单现金支付金额，详见支付金额
	protected int cash_fee;
//	现金支付货币类型	cash_fee_type	否	String(16)	CNY	货币类型，符合ISO4217标准的三位字母代码，默认人民币：CNY，其他值列表详见货币类型
	protected String cash_fee_type;
//	代金券金额	coupon_fee	否	Int	10	代金券金额<=订单金额，订单金额-代金券金额=现金支付金额，详见支付金额
//	代金券使用数量	coupon_count	否	Int	1	代金券使用数量
//	代金券类型	coupon_type_$n	否	Int	CASH	
//	CASH--充值代金券 
//	NO_CASH---非充值代金券
//	订单使用代金券时有返回（取值：CASH、NO_CASH）。$n为下标,从0开始编号，举例：coupon_type_$0
//	代金券ID	coupon_id_$n	否	String(20)	10000	代金券ID,$n为下标，从0开始编号
//	单个代金券支付金额	coupon_fee_$n	否	Int	100	单个代金券支付金额,$n为下标，从0开始编号
//	微信支付订单号	transaction_id	是	String(32)	1217752501201407033233368018	微信支付订单号
	protected String transaction_id;
//	商户订单号	out_trade_no	是	String(32)	1212321211201407033568112322	商户系统的订单号，与请求一致。
	protected String out_trade_no;
//	商家数据包	attach	否	String(128)	123456	商家数据包，原样返回
	protected String attach; // 格式: serverId:productId:playerId
//	支付完成时间	time_end	是	String(14)	20141030133525	支付完成时间，格式为yyyyMMddHHmmss，如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
	protected String time_end;
	
	@JSONField(serialize = false)
	protected Document ref;
	
	public String getReturn_code() {
		return return_code;
	}

	public void setReturn_code(String return_code) {
		this.return_code = return_code;
	}

	public String getReturn_msg() {
		return return_msg;
	}

	public void setReturn_msg(String return_msg) {
		this.return_msg = return_msg;
	}

	public String getAppid() {
		return appid;
	}

	public void setAppid(String appid) {
		this.appid = appid;
	}

	public String getMch_id() {
		return mch_id;
	}

	public void setMch_id(String mch_id) {
		this.mch_id = mch_id;
	}

	public String getSign() {
		return sign;
	}

	public void setSign(String sign) {
		this.sign = sign;
	}

	public String getResult_code() {
		return result_code;
	}

	public void setResult_code(String result_code) {
		this.result_code = result_code;
	}

	public String getOpenid() {
		return openid;
	}

	public void setOpenid(String openid) {
		this.openid = openid;
	}

	public String getTrade_type() {
		return trade_type;
	}

	public void setTrade_type(String trade_type) {
		this.trade_type = trade_type;
	}

	public String getBank_type() {
		return bank_type;
	}

	public void setBank_type(String bank_type) {
		this.bank_type = bank_type;
	}

	public int getTotal_fee() {
		return total_fee;
	}

	public void setTotal_fee(int total_fee) {
		this.total_fee = total_fee;
	}

	public String getFee_type() {
		return fee_type;
	}

	public void setFee_type(String fee_type) {
		this.fee_type = fee_type;
	}

	public int getCash_fee() {
		return cash_fee;
	}

	public void setCash_fee(int cash_fee) {
		this.cash_fee = cash_fee;
	}

	public String getCash_fee_type() {
		return cash_fee_type;
	}

	public void setCash_fee_type(String cash_fee_type) {
		this.cash_fee_type = cash_fee_type;
	}

	public String getTransaction_id() {
		return transaction_id;
	}

	public void setTransaction_id(String transaction_id) {
		this.transaction_id = transaction_id;
	}

	public String getOut_trade_no() {
		return out_trade_no;
	}

	public void setOut_trade_no(String out_trade_no) {
		this.out_trade_no = out_trade_no;
	}

	public String getAttach() {
		return attach;
	}

	public void setAttach(String attach) {
		this.attach = attach;
	}

	public String getTime_end() {
		return time_end;
	}

	public void setTime_end(String time_end) {
		this.time_end = time_end;
	}
	
	@JSONField(serialize = false)
	public boolean isSuccess() {
		return WeiXinApi.RESPONSE_SUCC.equals(return_code) && WeiXinApi.RESPONSE_SUCC.equals(result_code);
	}
	
	protected String verifySign;
	public boolean verify() {
		if (verifySign == null) {
			verifySign = WeiXinApi.makeSign(ref);
		}
		return sign != null && sign.equals(verifySign);
	}

	@Override
	public String toString() {
		return "PayResult [return_code=" + return_code + ", return_msg="
				+ return_msg + ", appid=" + appid + ", mch_id=" + mch_id
				+ ", sign=" + sign + ", result_code=" + result_code
				+ ", openid=" + openid + ", trade_type=" + trade_type
				+ ", bank_type=" + bank_type + ", total_fee=" + total_fee
				+ ", fee_type=" + fee_type + ", cash_fee=" + cash_fee
				+ ", cash_fee_type=" + cash_fee_type + ", transaction_id="
				+ transaction_id + ", out_trade_no=" + out_trade_no
				+ ", attach=" + attach + ", time_end=" + time_end + "]";
	}
	
	// response(xml) to weixin
	public static String response(String code, String msg) {
		Element root = new Element("xml");
		Document doc = new Document(root);
		root.addContent(new Element("return_code").setText(code));
		if (StringUtils.isNotEmpty(msg)) {
			root.addContent(new Element("return_msg").setText(msg));
		}
		XMLOutputter out = new XMLOutputter();
		Format f = Format.getRawFormat();
		f.setEncoding(IOUtils.UTF8);
		out.setFormat(f);
		return out.outputString(doc);
	}
	
	public String refToString() {
		XMLOutputter out = new XMLOutputter();
		Format f = Format.getRawFormat();
		f.setEncoding(IOUtils.UTF8);
		out.setFormat(f);
		return out.outputString(ref);
	}

	public static PayResult parse(byte[] req) {
		Reader reader = null;
		String s = null;
		try {
			s = CommonUtils.decodeURL(StringUtils.bytesToString(req, IOUtils.UTF8), IOUtils.UTF8);
			SAXBuilder builder = new SAXBuilder();
			Document doc = builder.build(reader = new StringReader(s));
			Element root = doc.getRootElement();
			PayResult pr = new PayResult();
			pr.ref = doc;
			pr.return_code = root.getChildText("return_code");
			pr.return_msg = root.getChildText("return_msg");
			pr.appid = root.getChildText("appid");
			pr.mch_id = root.getChildText("mch_id");
			pr.sign = root.getChildText("sign");
			pr.result_code = root.getChildText("result_code");
			pr.openid = root.getChildText("openid");
			pr.trade_type = root.getChildText("trade_type");
			pr.bank_type = root.getChildText("bank_type");
			pr.total_fee = NumberUtils.parseInt(root.getChildText("total_fee"));
			pr.fee_type = root.getChildText("fee_type");
			pr.cash_fee = NumberUtils.parseInt(root.getChildText("cash_fee"));
			pr.cash_fee_type = root.getChildText("cash_fee_type");
			pr.transaction_id = root.getChildText("transaction_id");
			pr.out_trade_no = root.getChildText("out_trade_no");
			pr.attach = root.getChildText("attach");
			pr.time_end = root.getChildText("time_end");
			return pr;
		} catch (Exception e) {
			LOG.error("[WEIXIN PAYRESULT PARSE ERROR]" + s);
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(reader);
		}
		return null;
	}
	
}
